;(function() {

  //DEPENDENCIES
  //Global object (window in the browser)
  var root = this;

  var $, _, Backbone;

  //CommonJS
  if (typeof require !== 'undefined') {
    $ = require('jquery');
    _ = require('underscore');
    Backbone = require('backbone');
  }

  //Browser
  else {
    $ = root.jQuery;
    _ = root._;
    Backbone = root.Backbone;
  }

//SOURCE
  
//==================================================================================================
//FORM
//==================================================================================================
  
var Asset = (function() {

  return Backbone.View.extend({

	    /**
	     * Creates a new form
	     *
	     * @param {Object} options
	     * @param {Model} [options.model]                 Model the form relates to. Required if options.data is not set
	     * @param {Object} [options.data]                 Date to populate the form. Required if options.model is not set
	     * @param {String[]} [options.fields]             Fields to include in the form, in order
	     * @param {String[]|Object[]} [options.fieldsets] How to divide the fields up by section. E.g. [{ legend: 'Title', fields: ['field1', 'field2'] }]        
	     * @param {String} [options.idPrefix]             Prefix for editor IDs. By default, the model's CID is used.
	     * @param {String} [options.template]             Asset template key/name
	     * @param {String} [options.fieldsetTemplate]     Fieldset template key/name
	     * @param {String} [options.fieldTemplate]        Field template key/name
	     *
	     * @return {Asset}
	     */
	    initialize: function(options) { 
	      //Check templates have been loaded
	      //if (!Asset.templates.block) throw new Error('Templates not loaded');

	      //Get the schema
	      var m = this.model;
	      
	      this.schema = (function() {
	        if (options.schema) return options.schema;
	      
	        var model = m;//options.model;
	        if (!model) throw new Error('Could not find model');
	      
	        //if (_.isFunction(model.schema)) return model.schema();	      
	        //return model.schema;
	        
	        if (_.isFunction(model.get("schema"))) return model.get("schema")();
		      
	        return model.get("schema");
	      })();

	      //Option defaults
	      options = _.extend({
	        template: 'block',
	        sectionTemplate: 'section'
	      }, options);

	      //Determine fieldsets
	      if (!options.sections) {
	        var sections = options.sections || _.keys(this.schema.sections);
	        options.sections = sections;//[{ sections: sections }];//////////////////////
	      }
	      
	      //Store main attributes
	      this.options = options;
	      this.data = options.data;
	      this.fields = {};
	      
	      this.pageType = this.schema.type === undefined?"":this.schema.type;
	      this.module = this.schema.module === undefined?"":this.schema.module;
	      var field_id = "";	      
		  if(this.module != ""){
			  field_id = this.module + "_";
		  };
		  this.module_id = field_id;
		  
	      this.forms = {}, this.tables = {};
	    },
	    
	    getForms: function(){
	    	return this.forms;
	    },
	    
	    getTables: function(){
	    	return this.tables;
	    },

	    /**
	     * Renders the form and all fields
	     */
	    render: function() {
	      var self = this,
	          options = this.options,
	          template = Backbone.Asset.templates[options.template];
	      
	      //Create el from template
	      var $page = $(template({
	    	navs: '<b class="nav-tmp"></b>',
	        sections: '<b class="bbf-tmp"></b>'
	      }));

	      //Render fieldsets
	      var $sectionContainer = $('.bbf-tmp', $page), $navContainer = $('.nav-tmp', $page);

	      _.each(options.sections, function(key, section) {
	    	  $navContainer.append(self.renderNav(key));
	    	  $sectionContainer.append(self.renderSection(key, section, options.readOnly));//////////////////
	      });
	      
	      $navContainer.children().unwrap();
	      $sectionContainer.children().unwrap();

	      //Set the template contents as the main element; removes the wrapper element
	      this.setElement($page);

	      return this;
	    },
	    
	    getItemSchema: function(key){
	    	var schema = this.schema.sections, itemSchema = (function() {
		          //Return a normal key or path key
			      if (schema[key]) return schema[key];
			  })();
	    	
	    	if (!itemSchema) throw "Section '"+ key +"' not found in schema";
	    	
	    	return itemSchema;
	    },
	    
	    renderNav: function(key){
	    	var itemSchema = this.getItemSchema(key), alias = itemSchema.alias === undefined?itemSchema.title:itemSchema.alias, module_id = this.module_id;    	
	    	return $("<li><a href=\"#sec_" + module_id + key + "\">" + alias + "</a></li>");
	    },

	    renderSection: function(key, section, readOnly) {
	      var self = this,
	          template = Asset.templates[this.options.sectionTemplate],
	          itemSchema = this.getItemSchema(key),
	          module = this.module,
	          module_id = this.module_id,
	          id = module_id + key;

	      //Concatenating HTML as strings won't work so we need to insert field elements into a placeholder
	      var $fieldset = $(template({
	    	key: id,
	    	title: itemSchema.title,
	        content: '<b class="bbf-tmp-fields"></b>'
	      }));

	      var $fieldsContainer = $('.bbf-tmp-fields', $fieldset);
	      $fieldsContainer.append("<div class=\"ph_container\"></div>");	      
	      
	      var forms = this.forms, tables = this.tables, pageType = this.pageType;
	      var components = itemSchema.components, ph = $fieldsContainer.find("div.ph_container");
	      var i = 0;
	      _.each(components, function(item, index){
	    	  var type = item.type;
	    	  if(type === "form"){
		    	  i++;
	    		  var form = forms[key + "_" + i] = generateForm(item.contents, key + "_" + i, readOnly, module);
	    		  ph/*.attr("id", "container_" + id)*/.append(form.el);
	    	  }else if(type === "table"){
	    		  if(index != 0){
	    			  ph.append("<div class=\"table-sep\"></div>");
	    		  }
	    		  var table = tables[item.id] = generateTable(pageType, item.id, readOnly, {readOnly: item.readOnly, caption: item.caption, cols: item.cols, actions: item.actions, history: item.history});
	    		  ph/*.attr("id", "container_" + key)*/.append("<div class=\"table-caption\">"+ item.caption +"</div>").append(table.el);
	    	  }
	      });
	      
	      $fieldsContainer = $fieldsContainer.children().unwrap();

	      return $fieldset;
	    },
	    
	    setValue: function(data){
	    	//data --- json format
	    	if(data != null){
				var props = data.properties, forms = this.getForms(), tables = this.getTables();

				if (forms) {
					var keys = _.keys(forms);
					_.each(keys, function(key) {
						forms[key].setValue(props, tables);
					});
				}
			
				if (tables) {
					var keys = _.keys(tables);
					_.each(keys, function(key) {
						if (props[key]) {
							tables[key].setValue(props[key]);
						} else {
							tables[key].setValue([]);
						}
					});
				}
	    	}
	    },
		  
		activate: function(flag){
			
			var forms = this.getForms(), tables = this.getTables();

			if(forms){
				_.each(forms, function(form, key){
					form.activate(flag);
				});
			}
			
			if(tables){
				_.each(tables, function(table, key){
					table.activate(flag);
				});
			}
		}
	      
	  });

})();

Asset.helpers = (function() {

	  var helpers = {};
	  
	  /**
	   * This function is used to transform the key from a schema into the title used in a label.
	   * (If a specific title is provided it will be used instead).
	   * 
	   * By default this converts a camelCase string into words, i.e. Camel Case
	   * If you have a different naming convention for schema keys, replace this function.
	   * 
	   * @param {String}  Key
	   * @return {String} Title
	   */
	  helpers.keyToTitle = function(str) {
	    //Add spaces
	    str = str.replace(/([A-Z])/g, ' $1');

	    //Uppercase first character
	    str = str.replace(/^./, function(str) { return str.toUpperCase(); });

	    return str;
	  };

	  /**
	   * Helper to compile a template with the {{mustache}} style tags. Template settings are reset
	   * to user's settings when done to avoid conflicts.
	   * @param {String}    Template string
	   * @return {Template} Compiled template
	   */
	  helpers.compileTemplate = function(str) {
	      //Store user's template options
	      var _interpolateBackup = _.templateSettings.interpolate;

	      //Set custom template settings
	      _.templateSettings.interpolate = /\{\{(.+?)\}\}/g;

	      var template = _.template(str);

	      //Reset to users' template settings
	      _.templateSettings.interpolate = _interpolateBackup;

	      return template;
	  };

	  /**
	   * Helper to create a template with the {{mustache}} style tags.
	   * If context is passed in, the template will be evaluated.
	   * @param {String}             Template string
	   * @param {Object}             Optional; values to replace in template
	   * @return {Template|String}   Compiled template or the evaluated string
	   */
	  helpers.createTemplate = function(str, context) {
	    var template = helpers.compileTemplate(str);
	    
	    if (!context) {
	      return template;
	    } else {
	      return template(context);
	    }
	  };
	  

	  /**
	   * Sets the template compiler to the given function
	   * @param {Function} Template compiler function
	   */
	  helpers.setTemplateCompiler = function(compiler) {
	    helpers.compileTemplate = compiler;
	  };
	  
	  
	  /**
	   * Sets the templates to be used.
	   * 
	   * If the templates passed in are strings, they will be compiled, expecting Mustache style tags,
	   * i.e. <div>{{varName}}</div>
	   *
	   * You can also pass in previously compiled Underscore templates, in which case you can use any style
	   * tags.
	   * 
	   * @param {Object} templates
	   * @param {Object} classNames
	   */
	  helpers.setTemplates = function(templates, classNames) {
	    var createTemplate = helpers.createTemplate;
	    
	    Asset.templates = Asset.templates || {};
	    Asset.classNames = Asset.classNames || {};
	    
	    //Set templates, compiling them if necessary
	    _.each(templates, function(template, key, index) {
	      if (_.isString(template)) template = createTemplate(template);
	      
	      Asset.templates[key] = template;
	    });
	    
	    //Set class names
	    _.extend(Asset.classNames, classNames);
	  };	  	

	  return helpers;

	})();

	Asset.setTemplates = Asset.helpers.setTemplates;
	Asset.setTemplateCompiler = Asset.helpers.setTemplateCompiler;

	Asset.templates = {};	
	
	  //EXPORTS
	  //CommonJS
	  if (typeof module == 'object' && module.exports) {
	    module.exports = Asset;
	  }

	  //Browser
	  else {
	    Backbone.Asset = Asset;
	  }

}).call(this);


function generateForm(schema, key, readOnly, module){		

	  var form = new Backbone.Form({
		  model: new ItemModel({components: schema}),
		  readOnly: readOnly,
		  name: key,
		  module: module
	  }).render();
	  
	  return form;
}

function generateTable(pageType, key, readOnly, table){
	
	var r = table.readOnly === undefined?false:table.readOnly;
	
	var t = new BTS.SortableTable({
		pageType: pageType,
		identity: key,
		restriction: readOnly,// global readOnly
        readOnly: r,// self readOnly
        col: table.cols,
        operations: table.actions,
        history: table.history
    }).render(readOnly);
	
	return t;
}